/*
 * SPDX-License-Identifier: BSD-3-Clause
 * Copyright (c) Huawei Technologies Co., Ltd. 2021-2023. All rights reserved.
 * Description   : udk rwlock header file
 */

#ifndef UDK_RWLOCK_H
#define UDK_RWLOCK_H

#ifndef UDK_ARCH_ARM64
#include <emmintrin.h>
#else
#include <stdint.h>
#endif

typedef struct {
    volatile int32_t cnt; /* -1 when W lock held, > 0 when R locks held. */
} udk_rwlock_t;

static inline void udk_rwlock_init(udk_rwlock_t *rwl)
{
    rwl->cnt = 0;
}

static inline void udk_pause(void)
{
#ifdef UDK_ARCH_ARM64
    asm volatile("yield" ::: "memory");
#else
    _mm_pause();
#endif
}


static inline void udk_rwlock_write_lock(udk_rwlock_t *rwl)
{
    int32_t x;
    int success = 0;

    while (success == 0) {
        x = __atomic_load_n(&rwl->cnt, __ATOMIC_RELAXED);
        /* write or read lock is held */
        if (x != 0) {
            udk_pause();
            continue;
        }
        success = __atomic_compare_exchange_n(&rwl->cnt, &x, -1, 1, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED);
    }
}

static inline void udk_rwlock_write_unlock(udk_rwlock_t *rwl)
{
    __atomic_store_n(&rwl->cnt, 0, __ATOMIC_RELEASE);
}

static inline void udk_rwlock_read_lock(udk_rwlock_t *rwl)
{
    int32_t x;
    int success = 0;

    while (success == 0) {
        x = __atomic_load_n(&rwl->cnt, __ATOMIC_RELAXED);
        /* write lock is held */
        if (x < 0) {
            udk_pause();
            continue;
        }
        success = __atomic_compare_exchange_n(&rwl->cnt, &x, x + 1, 1, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED);
    }
}

static inline void udk_rwlock_read_unlock(udk_rwlock_t *rwl)
{
    __atomic_fetch_sub(&rwl->cnt, 1, __ATOMIC_RELEASE);
}

#endif